#include "MaterialSerializer.h"
#include <SERender/Resourses/Material.h>
#include <yaml-cpp/yaml.h>
#include <SESerializeYAML/Util.h>
#include <SERender/Resourses/Loader/ShaderLoader.h>
#include <SERender/Resourses/UniformBuffer.h>
#include <SEngine/Ecs/TypeRegister.h>
#include <SERender/Resourses/Loader/TextureLoader.h>
#include <SEngine/Render/RenderFeature/UBORenderFeature.h>
#include <SECore/Manager.h>

#include <fstream>
#include <SEDebug/Log/Log.h>

namespace SEngine
{

    std::string Serializer<Material>::Serialize(Material & object)
    {
        YAML::Node node;
        try
        {
            auto & pso = object.GetPSO();
            node["Material"]["PSO"]["Blend"] = pso.blend;
            node["Material"]["PSO"]["CullFace"] = pso.cullFace;
            node["Material"]["PSO"]["CullFront"] = pso.cullFront;
            node["Material"]["PSO"]["Depth"] = pso.depth;
            node["Material"]["Shader"]["Path"] = object.m_shader.Name();
            for (auto & [name, varint] : object.m_uniformDatas) {
                if (object.m_shader.Value().HasUniform(name)) {
                    std::visit([&](auto & data) {
                        ElementSerialize<std::decay_t<decltype(data)>>(node["Material"]["Shader"], name, data);
                    }, varint);
                }
            }
        }
        catch(const std::exception& e)
        {
            Log::GetInstance().Warn("Material Serialize: {}", e.what());
        }
        return YAMLNodeToString(node);
    }

    void Serializer<Material>::Deserialize(const std::string& str, Material & object)
    {
        try
        {
            YAML::Node node = YAML::Load(str);
            auto & pso = object.GetPSO();
            pso.blend = node["Material"]["PSO"]["Blend"].as<bool>();
            pso.cullFace = node["Material"]["PSO"]["CullFace"].as<bool>();
            pso.cullFront = node["Material"]["PSO"]["CullFront"].as<bool>();
            pso.depth = node["Material"]["PSO"]["Depth"].as<bool>();
            auto shaderPath = node["Material"]["Shader"]["Path"].as<std::string>(); 
            if (!shaderPath.empty()) {
                object.SetShader(Manager::GetInstance().Get<Shader>(shaderPath).value());
            } else {
                object.SetShader(Manager::GetInstance().Get<Shader>("PBR").value());
            }
            object.m_shader.Value().ForeachUniforms([&](const std::string & name, auto & type) {
                try {
                    std::decay_t<decltype(type)> data = type; // 先设置默认值
                    ElementDeserialize<std::decay_t<decltype(type)>>(node["Material"]["Shader"], name, data);
                    object.Set(name, data);
                } catch(...) {
                    Log::GetInstance().Warn("Unload uniform {}", name);
                }
            });
        }
        catch(const std::exception& e)
        {
            Log::GetInstance().Warn("Material Deserialize: {}", e.what());
        }
    }

} // namespace SEngine
